﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Windows;

namespace Xceed.Silverlight.ListBox.PositioningFunction
{
  internal class HorizontalPositioning : PositioningFunctionBase
  {
    #region Constructors

    internal HorizontalPositioning()
    {
    }

    #endregion

    #region DisplayName Property

    internal override string DisplayName
    {
      get
      {
        return "Horizontal Positioning function";
      }
    }

    #endregion

    #region IsScrollOffsetDifferenceBetweenPositionsSupported Property

    internal override bool IsScrollOffsetDifferenceBetweenPositionsSupported
    {
      get
      {
        return true;
      }
    }

    #endregion

    #region Internal Methods
    internal override void PrepareLayoutPass( double partialOffset, Size itemContainerSize, Size groupContainerSize )
    {
      m_firstContainerLayouted = false;
      m_cachedPartialOffset = partialOffset;
      m_cachedItemContainerSize = itemContainerSize;
      m_cachedGroupContainerSize = groupContainerSize;
    }

    internal override void PlaceContainerForward( Size container,
      bool isGroupContainer,
      out Point containerPosition, 
      out double containerProportionInViewport ,
      out int layoutProgressionPercentage,
      out double rotateAngle )
    {
      double containerWidth = container.Width;

      if( !m_firstContainerLayouted )
      {
        double pixelsPartialOffset = 0.0;
        if( isGroupContainer )
        {
          pixelsPartialOffset = m_cachedPartialOffset * ( containerWidth + m_cachedItemContainerSize.Width );
        }
        else
        {
          // In the case of an item, we just multiply by the item's height.
          pixelsPartialOffset = m_cachedPartialOffset * container.Width;
        }
        this.IndexPositionTopBottom = this.StartOffsetIndex - pixelsPartialOffset;
        this.IndexPositionBottomUp = this.StartOffsetIndex - pixelsPartialOffset;
        m_firstContainerLayouted = true;
      }

      rotateAngle = 0.0d;
      layoutProgressionPercentage = 100;
      containerPosition = new Point();
      containerPosition.X = this.IndexPositionTopBottom;
      containerPosition.Y = 0.0d;
      this.IndexPositionTopBottom += container.Width;
      containerProportionInViewport = this.GetContainerProportionInEffectiveViewport( container, containerPosition );
    }

    internal override void PlaceContainerBackward( Size container,
      bool isGroupContainer,
      out Point containerPosition,
      out double containerProportionInViewport,
      out int layoutProgressionPercentage,
      out double rotateAngle )
    {
      double containerWidth = container.Width;

      this.IndexPositionBottomUp -= container.Width;
      rotateAngle = 0.0d;
      layoutProgressionPercentage = 100;
      containerPosition = new Point();
      containerPosition.X = this.IndexPositionBottomUp;
      containerPosition.Y = 0.0d;
      containerProportionInViewport = this.GetContainerProportionInEffectiveViewport( container, containerPosition );
    }

    internal override Point GetBringIntoViewPositionForContainer( Size groupHeaderContainerSize,
      Size itemContainerSize,
      Size viewportSize,
      bool forwardScrolling )
    {
      double positionX = 0.0d;

      if( !forwardScrolling )
      {
        // If we are not forwardScrolling, we are "scrolling up".  This means
        // we have to position the container just after the sticky header.
        positionX = groupHeaderContainerSize.Width;
      }
      else
      {
        // If we are forward scrolling, we need to make the container
        // fully visible at the end of the viewport
        positionX = viewportSize.Width - ( itemContainerSize.Width );
      }

      // container's Y position for the horizontal positionning is always 0.
      return new Point( positionX, 0.0d );
    }

    internal override double GetScrollOffsetDifferenceBetweenPositions( Point originalPosition, Point targetPosition, Size containerSize )
    {
      double differenceInPixel = originalPosition.X - targetPosition.X;
      double differenceInScrollOffset = differenceInPixel / containerSize.Width;

      return differenceInScrollOffset;
    }

    internal override Rect GetCorrectedViewportVisibilityRect( Size stickyHeaderSize, Rect viewportVisibilityRect )
    {
      // In the case of horizontal positionning, we have to exclude
      // the sticky header from the viewport visibility rectangle because
      // containers under the sticky header are not visible.
      viewportVisibilityRect.X += stickyHeaderSize.Width;
      viewportVisibilityRect.Width -= stickyHeaderSize.Width;

      return viewportVisibilityRect;
    }

    internal override void UpdateEffectiveViewportSizeInPixels( Size newViewportSizeInPixels )
    {
      this.EffectiveViewportStart = 0.0d;
      this.EffectiveViewportEnd = newViewportSizeInPixels.Width;
      double viewportOversizedWidth = newViewportSizeInPixels.Width * 1.20;
      double oversizedInPixels = viewportOversizedWidth - newViewportSizeInPixels.Width;
      double nbPixelsToAddEachSide = oversizedInPixels / 2.0d;
      this.ExtendedViewportStart = this.EffectiveViewportStart - nbPixelsToAddEachSide;
      this.ExtendedViewportEnd = this.EffectiveViewportEnd + nbPixelsToAddEachSide;
      this.StartOffsetIndex = this.EffectiveViewportStart;

      this.ResetPositioningIndexes();
    }

    internal override void CorrectViewportSize( double itemsInViewport, ref Size viewport )
    {
      viewport.Width = itemsInViewport;
    }

    internal override Point PlaceStickyHeader( Point lastContainerOfTheGroupPosition, Size containerSize, out double angle )
    {
      Point stickyPostion = new Point( 0.0d, 0.0d );
      angle = 0.0d;
      stickyPostion.Y = lastContainerOfTheGroupPosition.Y;
      stickyPostion.X = lastContainerOfTheGroupPosition.X + m_cachedItemContainerSize.Width - containerSize.Width;
      stickyPostion.X = Math.Min( 0.0d, stickyPostion.X );
      return stickyPostion;
    }

    internal override double GetGroupHeaderProportion()
    {
      return m_cachedGroupContainerSize.Width / ( m_cachedGroupContainerSize.Width + m_cachedItemContainerSize.Width );
    }

    internal override Size GetCorrectedContainerSize( Size containerSize, Size viewportSize )
    {
      return new Size( containerSize.Width, Math.Min( viewportSize.Height, containerSize.Height ) );
    }
    #endregion

    #region Protected Internal Methods
    protected internal override double GetContainerProportionInEffectiveViewport( Size containerSize, Point containerPosition )
    {
      // 5 possible cases 
      // #1 container is completly out of the effective viewport (at the top) (0.0)
      // #2 container is completly out of the effective viewport (at the end) (0.0)
      // #3 container is completly in the effective viewport (1.0)
      // #4 container is partially in the effective viewport (at the top) (between 0.0 and 1.0)
      // #5 container is partially in the effective viewport (at the end) (between 0.0 and 1.0)
      double containerWidth = containerSize.Width;
      double containerStartPosition = containerPosition.X;
      double containerEndPosition = containerPosition.X + containerWidth;
      double sizeOfContainerInViewport = 0.0d;

      if( containerStartPosition < this.EffectiveViewportStart )
      {
        // Case #1 or #4
        if( containerEndPosition < this.EffectiveViewportStart )
        {
          // Case #1
          return 0.0d;
        }
        else
        {
          // Case #4
          sizeOfContainerInViewport = containerEndPosition - this.EffectiveViewportStart;
          return sizeOfContainerInViewport / containerWidth;
        }
      }
      else if( containerEndPosition > this.EffectiveViewportEnd )
      {
        // Case #2 or #5
        if( containerStartPosition > this.EffectiveViewportEnd )
        {
          // Case #2
          return 0.0d;
        }
        else
        {
          // Case #5
          sizeOfContainerInViewport = this.EffectiveViewportEnd - containerStartPosition;
          return sizeOfContainerInViewport / containerWidth;
        }
      }
      else
      {
        // Case 3
        return 1.0;
      }
    }
    #endregion

    #region Private Members
    private Size m_cachedItemContainerSize = new Size( 0.0d, 0.0d );
    private Size m_cachedGroupContainerSize = new Size( 0.0d, 0.0d );
    private bool m_firstContainerLayouted = false;
    private double m_cachedPartialOffset = 0.0d;
    #endregion

  }
}
